/**
YASRACS game client module.

YASTACS game
Copyright (C) 2012 Kalman Kiss, kiskami@freemail.hu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Contact author at: kiskami@freemail.hu or
Hungary, 8900 Zalaegerszeg
Kakukkfu u 4.
*/
module client.Client;

import std.stdio;
import std.conv;
import std.string;

import iCore;
import Common;

import ogreint.Timer;
import ceguiint.Window;

import server.GameServerInterface;

import ugen.UniversumData;
import client.GalaxyMap;

immutable string gamestartprogress_layout = "gamestartprogress.layout";
immutable string gamestartprogress_logmsgwindow = "GameStartProgressRoot/logmessages";
immutable string gamestartprogress_progressbar = "GameStartProgressRoot/progress";

immutable string server_error_pref = "SERVER_ERROR";
immutable string server_warn_pref = "SERVER_WARNING";

class Client : GameState {

	this(iCore core_, GameServerInterface serverint_, string servername_="") {
			debug writeln("Client ctor");
			core = core_;
			serverint = serverint_;
			servername = servername_;
			serverint.setProgressDelegate(&this.progress);
			serverint.setLogDelegate(&this.log);
			state = State.CREATED;
			runstate = RunState.NONE;
			create_new_game = false;

			progress_window = core.gui.loadLayout(gamestartprogress_layout);

	}

	void createNewGame(string name_, string seed_, GameDifficultyLevel level_, bool violence_) {
		create_new_game = true;
		game_name = name_;
		game_seed = seed_;
		game_level = level_;
		game_violence = violence_;
	};

	void initialize() {
		debug writeln("Client initialize");

		core.gui.add(progress_window);

		state = State.INITED;

		mouseshown = core.gui.showMouseCursor(false);

		setProgress(0);
		core.gui.setWidgetText(gamestartprogress_logmsgwindow,"");
	}

	void progress(float p) {
		//		write(" (",p*100,") ");
		setProgress(p>1?1:p);
		core.rendersystem.Update();
	}
	void log(string msg) {
		//		write("");
		appendToMsgs(msg);
		core.rendersystem.Update();
	}

	void bufferLogMsgs(bool b=true) {
		bufferedlogs = b;
	}

	void update(real elapsedTime) {
		if(state != State.INITED && state != State.RUNNING && state != State.PAUSED) {
			debug writeln("Client current state is ", state);
			throw new Exception("Client is in invalid state to run.");
		}

		switch(state) {
		case State.INITED :
			// épphogy betöltődtünk, indulhat a móka
			state = State.RUNNING;
			runstate = RunState.CONNECTING;
			break;
		case State.RUNNING :

			serverint.update(sessionid, elapsedTime);

			switch(runstate) {
				case RunState.CONNECTING : 
					debug writeln("Connecting to server...");
					log("Connecting to server...");
					// TODO username, password
					auto res = serverint.connectToServer(servername, "", "");
					if(wasServerError(res)) {
						logServerError(res, "Couldn't connect to server.");
						runstate = RunState.DISCONNECTED;
						// TODO
					} else {
						sessionid = res.session_id();

						if(wasServerWarning(res)) {
							logServerWarn(res, "Connected to server with warning.");
						}

						runstate = RunState.STARTING;
					}
					break;
				case RunState.STARTING :
					if(create_new_game) {
						debug writeln("Creating new game on server...");
						ServerResult res;
						{
							bufferLogMsgs(true); // buffer logs, because startNewGame may send us many little msgs
							scope (exit) {
								flushMsgBuf();
								bufferLogMsgs(false);
							}
							res = serverint.startNewGame(sessionid, game_name, game_seed, game_level, game_violence);
						}
						if(wasServerError(res)) {
							logServerError(res, "Couldn't start a new game on server.");
							runstate = RunState.DISCONNECTED;
							break;
						} else {
							if(wasServerWarning(res)) {
								logServerWarn(res, "Started a new game on server with warning.");
							}

						}
						debug writeln("Done.");
					}

					debug writeln("Getting universum data from server...");
					auto res = serverint.getUniversumData(sessionid);
					if(wasServerError(res)) {
						logServerError(res, "Couldn't get universum data from server.");
						serverint.disconnectFromServer(sessionid);
						runstate = RunState.DISCONNECTED;
						break;
					} else {
						if(wasServerWarning(res)) {
							logServerWarn(res, "Started got universum data from server with warning.");
						}
					}

					ud = Universum.createFromJson(res.data);
					debug writeln("Done.\nCreating galaxy map...");
					gm = new GalaxyMap(core, ud);
					debug writeln("Done.");

					runstate = RunState.RUNNING;
					core.gui.remove(progress_window);

					// show the galaxy map for now
					if(!gm.shown) gm.show();

					break;
				case RunState.RUNNING :

					// left mouse click opens galaxy map
					if(core.input.leftmousebtnpressed) gm.show();

					gm.input(elapsedTime);

					break;
				case RunState.DISCONNECTED :
					// TODO
					break;
				default:
					throw new Exception("Invalid runstate in Client update!");
			}
			break;	
		default:
			throw new Exception("Invalid state in Client update!");
		}
	}

	void pause() {
		state = State.PAUSED;
	}

	void restart() {
		state = State.RUNNING;
	}

	void deinitialize() {
		debug writeln("Client deinitialize");

		core.gui.remove(progress_window);
		core.gui.showMouseCursor(mouseshown);

		// screen elements
		state = State.DEINITED;
		runstate = RunState.NONE;
	}

private:

	void setProgress(float p) {
		core.gui.setProp(gamestartprogress_progressbar,"CurrentProgress",to!string(p));
	}

	immutable uint BUFFERLENGTH = 460;
	void appendToMsgs(string msg) {

		string wtext = core.gui.getWidgetText(gamestartprogress_logmsgwindow);
		uint caratpos = wtext.length;
		if(bufferedlogs) {
			logbuffer ~= msg;
			if(logbuffer.length >= BUFFERLENGTH) {
				core.gui.setWidgetText(gamestartprogress_logmsgwindow, wtext ~ logbuffer);
				caratpos += logbuffer.length;
				logbuffer = "";
			} 
		} else {
			core.gui.setWidgetText(gamestartprogress_logmsgwindow, wtext ~ msg);
			caratpos += msg.length;
		}

		// force text reformat
//		core.gui.setProp(gamestartprogress_logmsgwindow, "WordWrap", "True");
		core.gui.setProp(gamestartprogress_logmsgwindow,"CaratIndex", to!string(caratpos));
	}

	void flushMsgBuf() {
		if(bufferedlogs) {
			string wtext = core.gui.getWidgetText(gamestartprogress_logmsgwindow);
			uint caratpos = wtext.length + logbuffer.length;
			core.gui.setWidgetText(gamestartprogress_logmsgwindow, logbuffer);
			core.gui.setProp(gamestartprogress_logmsgwindow,"CaratIndex", to!string(caratpos));
		}
	}

	void logServerError(ServerResult res, string msg) {
		log(format("%s [%s] %s [data:%s]", server_error_pref, to!string(res.result_code), msg, res.data));
	}

	void logServerWarn(ServerResult res, string msg) {
		log(format("%s [%s] %s [data:%s]", server_warn_pref, to!string(res.result_code), msg, res.data));
	}

	iCore core;

	GameServerInterface serverint;
	string servername;

	enum State {
		CREATED,
		INITED,
		RUNNING,
		PAUSED,
		DEINITED
	};

	enum RunState {
		NONE
		, DISCONNECTED
		, CONNECTING
		, STARTING
		, RUNNING
	};

	State state;
	RunState runstate;

	SessionId sessionid;

	Window progress_window;

	bool mouseshown;

	bool create_new_game;
	string game_name, 
			game_seed;
	GameDifficultyLevel game_level;
	bool game_violence;

	bool bufferedlogs;
	string logbuffer;

	Universum ud;
	GalaxyMap gm;
}
